- Published on
RAG检索增强生成(三)
- Authors

- Name
- 游戏人生
Chat History
ChatMessageHistory
chat history 是一组 Message 子类对象组成的列表,Message 子类可能是 HumanMessage、AIMessages。chat history 负责记录聊天历史。memory 模块负责将聊天记录的摘要或者最近几条聊天记录,转换为向量数据库中的向量,存储到向量数据库中。
创建一个 history 对象:
import { ChatMessageHistory } from "langchain/stores/message/in_memory";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
const history = new ChatMessageHistory();
添加消息:
await history.addMessage(new HumanMessage("你好"));
await history.addMessage(new AIMessage("你好啊!"));
获取历史记录
const messages = await history.getMessages();
console.log(messages);
输出内容如下:
[
HumanMessage {
lc_serializable: true,
lc_kwargs: { content: "你好", additional_kwargs: {}, response_metadata: {} },
lc_namespace: [ "langchain_core", "messages" ],
content: "你好",
name: undefined,
additional_kwargs: {},
response_metadata: {}
},
AIMessage {
lc_serializable: true,
lc_kwargs: { content: "你好啊!", additional_kwargs: {}, response_metadata: {} },
lc_namespace: [ "langchain_core", "messages" ],
content: "你好啊!",
name: undefined,
additional_kwargs: {},
response_metadata: {}
}
]
手动维护 chat history
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { ChatAlibabaTongyi } from "@langchain/community/chat_models/alibaba_tongyi";
const chatModel = new ChatAlibabaTongyi({
model: "qwen-turbo", // Available models: qwen-turbo, qwen-plus, qwen-max
temperature: 1,
});
const prompt = ChatPromptTemplate.fromMessages([
["system", `你是一个乐于助人的助手,尽你所能地回答所有问题。
你很健谈,能从上下文中提供大量具体细节。
如果你不知道问题的答案,那就如实地说你不知道。`],
new MessagesPlaceholder("history_message"),
]);
const chain = prompt.pipe(chatModel);
其中 MessagesPlaceholder 创建一个名为 history_message 的插槽,chain 中对应的参数将会替换这部分。
创建一个 chat history,然后向其中添加一条历史记录:
import { ChatMessageHistory } from "langchain/stores/message/in_memory";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
const history = new ChatMessageHistory();
await history.addMessage(new HumanMessage("你好,我是叮当猫"));
const res1 = await chain.invoke({
history_message: await history.getMessages()
});
console.log(res1);
返回内容如下:
AIMessage {
lc_serializable: true,
lc_kwargs: {
content: "你好,叮当猫!很高兴见到你。如果你有任何问题或者需要帮助,请随时告诉我。",
tool_calls: [],
invalid_tool_calls: [],
additional_kwargs: {},
response_metadata: {}
},
lc_namespace: [ "langchain_core", "messages" ],
content: "你好,叮当猫!很高兴见到你。如果你有任何问题或者需要帮助,请随时告诉我。",
name: undefined,
additional_kwargs: {},
response_metadata: {
tokenUsage: { promptTokens: 64, completionTokens: 20, totalTokens: 84 }
},
tool_calls: [],
invalid_tool_calls: []
}
添加一条人类新提问:
await history.addMessage(res1)
await history.addMessage(new HumanMessage("我是谁"));
获取回答,传入history
const res2 = await chain.invoke({
history_message: await history.getMessages(),
});
console.log(res2);
返回内容如下:
AIMessage {
lc_serializable: true,
lc_kwargs: {
content: "你自称是叮当猫,这通常指的是卡通角色《蓝皮鼠和大脸猫》中的角色,或者在一些衍生作品中,也可能指代哆啦A梦(Doraemon)系列里的叮当猫(Ding Dong Bell)。如果你在玩某个角色扮演游戏或"... 20 more characters,
tool_calls: [],
invalid_tool_calls: [],
additional_kwargs: {},
response_metadata: {}
},
lc_namespace: [ "langchain_core", "messages" ],
content: "你自称是叮当猫,这通常指的是卡通角色《蓝皮鼠和大脸猫》中的角色,或者在一些衍生作品中,也可能指代哆啦A梦(Doraemon)系列里的叮当猫(Ding Dong Bell)。如果你在玩某个角色扮演游戏或"... 20 more characters,
name: undefined,
additional_kwargs: {},
response_metadata: {
tokenUsage: { promptTokens: 96, completionTokens: 73, totalTokens: 169 }
},
tool_calls: [],
invalid_tool_calls: []
}
自动维护 chat history
使用 RunnableWithMessageHistory 给任意 chain 包裹一层,就能添加聊天记录管理的能力。
import { RunnableWithMessageHistory } from "@langchain/core/runnables";
const chatModel = new ChatAlibabaTongyi({
model: "qwen-turbo", // Available models: qwen-turbo, qwen-plus, qwen-max
temperature: 1,
});
const prompt = ChatPromptTemplate.fromMessages([
["system", "你是一个乐于助人的助手,尽你所能地回答所有问题。"],
new MessagesPlaceholder("history_message"),
["human","{input}"]
]);
const history = new ChatMessageHistory();
const chain = prompt.pipe(chatModel)
const chainWithHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: (_sessionId) => history,
inputMessagesKey: "input",
historyMessagesKey: "history_message",
});
RunnableWithMessageHistory 参数含义:
- runnable: 需要被包裹的 chain,可以是任意 chain
- getMessageHistory: 接收一个函数,函数需要根据传入的 _sessionId,去获取对应的 ChatMessageHistory 对象
- inputMessagesKey:用户传入的信息 key 的名称,因为 RunnableWithMessageHistory 要自动记录用户和 llm 发送的信息,所以需要在这里声明用户传入信息的 key
- historyMessagesKey: 聊天记录在 prompt 中的 key,因为要自动的把聊天记录注入到 prompt 中
- outputMessagesKey: 只有一个输出就省略,如果有多个输出需要指定哪个是 llm 的回复,也就是需要存储的信息
直接调用这个 chain,历史记录会自动保存,这里除了正常 invoke 传入的参数外,还需要指定当前对话的 sessionId。
const res1 = await chainWithHistory.invoke({
input: "你好,我是叮当猫",
},{
configurable: { sessionId: "none" }
});
console.log(res1);
再次提问:
const res2 = await chainWithHistory.invoke({
input: "我是谁",
},{
configurable: { sessionId: "none" }
});
console.log(res2);
返回内容如下:
AIMessage {
lc_serializable: true,
lc_kwargs: {
content: "你自称是叮当猫,这通常是指卡通角色《蓝猫淘气三千问》中的主角之一,或者指《哆啦A梦》(Doraemon)中的重要角色,哆啦A梦的神奇小口袋里的道具“叮当”(铜锣)。如果你在玩角色扮演游戏或者有其他特定"... 36 more characters,
tool_calls: [],
invalid_tool_calls: [],
additional_kwargs: {},
response_metadata: {}
},
lc_namespace: [ "langchain_core", "messages" ],
content: "你自称是叮当猫,这通常是指卡通角色《蓝猫淘气三千问》中的主角之一,或者指《哆啦A梦》(Doraemon)中的重要角色,哆啦A梦的神奇小口袋里的道具“叮当”(铜锣)。如果你在玩角色扮演游戏或者有其他特定"... 36 more characters,
name: undefined,
additional_kwargs: {},
response_metadata: {
tokenUsage: { promptTokens: 72, completionTokens: 90, totalTokens: 162 }
},
tool_calls: [],
invalid_tool_calls: []
}
自动生成 chat history 摘要
实现一个自动对当前聊天历史记录进行总结,然后让 llm 根据总结的信息回复用户的 chain。
首先实现一个总结 chain,这个 chain 接受两个参数:
- summary,上一次总结的信息
- new_lines,用户和 llm 新的回复
返回值是一个纯文本的信息,根据历史的 summary 信息和用户新的对话生成的新 summary。
import { ChatAlibabaTongyi } from "@langchain/community/chat_models/alibaba_tongyi";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { RunnableSequence } from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";
const summaryModel = new ChatAlibabaTongyi({
model: "qwen-turbo", // Available models: qwen-turbo, qwen-plus, qwen-max
temperature: 1,
});
const summaryPrompt = ChatPromptTemplate.fromTemplate(`
逐步总结所提供的对话内容,在之前的总结基础上添加新的总结
当前摘要:
{summary}
新的对话方式:
{new_lines}
新摘要:
`);
const summaryChain = RunnableSequence.from([
summaryPrompt,
summaryModel,
new StringOutputParser(),
]);
调用:
const newSummary = await summaryChain.invoke({
summary: "",
new_lines: "我是叮当猫"
});
再次调用,并传入上次的 summary
await summaryChain.invoke({
summary: newSummary,
new_lines: "我会飞"
});
输出内容如下:
'在先前的对话总结中,我们主要探讨了整体情况和可能的话题范围。随着新角色"我是叮当猫"的加入,对话可能转向与超自然能力、冒险、童话世界或者科幻元素相关的内容,特别是如果"我会飞"的能力成为讨论的核心。然'... 40 more characters
如此就实现了一个渐进式总结历史聊天记录的 chat bot,然后以此为基础构建一个 chat bot,其会自动将聊天记录进行 summary,并且传递给 llm 作为上下文。
首先创建一个基础的 prompt 模板,和用于存储聊天记录的 ChatMessageHistory,并且创建一个 summary 字符串,用来存储用户之前聊天记录的总结信息。
import { ChatMessageHistory } from "langchain/stores/message/in_memory";
const chatModel = new ChatAlibabaTongyi({
model: "qwen-turbo", // Available models: qwen-turbo, qwen-plus, qwen-max
temperature: 1,
});
const chatPrompt = ChatPromptTemplate.fromMessages([
["system", `你是一个乐于助人的助手,尽你所能地回答所有问题。
以下是聊天记录摘要:
{history_summary}
`],
["human","{input}"]
]);
let summary = ""
const history = new ChatMessageHistory();
实现完整的 chain:
import { RunnablePassthrough } from "@langchain/core/runnables";
const chatChain = RunnableSequence.from([
{
input: new RunnablePassthrough({
func: (input) => history.addUserMessage(input)
})
},
RunnablePassthrough.assign({
history_summary: () => summary
}),
chatPrompt,
chatModel,
new StringOutputParser(),
new RunnablePassthrough({
func: async (input) => {
history.addAIChatMessage(input);
const messages = await history.getMessages();
const new_lines = getBufferString(messages);
const newSummary = await summaryChain.invoke({
summary,
new_lines
});
history.clear();
summary = newSummary;
}
})
]);
RunnableMap 并行的执行多个 runnable 对象,然后返回结果对象。
import { RunnableMap } from "@langchain/core/runnables"
const mapChain = RunnableMap.from({
a: () => "a",
b: () => "b"
})
const res = await mapChain.invoke()
// { a: "a", b: "b" }
函数也是一种 runnable 对象,这两个函数是并行执行的。这两个函数换成任意 runnable 对象,例如两个 chain 即会并行执行这两个 chain,并且返回相应的结果。
在 RunnableSequence 的数组中,如果有 object 类型的值,会被自动转换成 RunnableMap,也就是 chain 中第一个 object 对象本质上是新建了一个 RunnableMap。
RunnablePassthrough 是一个特殊的 runnable,它不会执行任何操作,而是直接返回输入。可以理解是 runnable chain 中的特殊节点。
使用代码①
new RunnablePassthrough({func: (input)=> void})
的目的:
- 如果只写 new RunnablePassthrough(),就是把用户输入的 input 再传递到下一个 runnable 节点中,不做任何操作。因为 RunnableMap 返回值是对其中每个 chain 的执行,然后将返回值作为结果传递给下一个 runnable 节点,如果不对 input 使用 RunnablePassthrough 则下个节点就拿不到 input 的值
- ① 中的 func 函数是在传递 input 的过程中,执行一个函数,这个函数返回值是 void,即无论其内容是什么,都不会对 input 造成影响。
测试 chain:
const res1 = await chatChain.invoke("我现在饿了");
console.log(res1);
输出内容如下:
如果你饿了,可以考虑吃点东西。你可以选择制作简单的三明治、热汤或者是一碗方便面,如果你在家;外出的话,可以去附近的餐馆、便利店或者点外卖。别忘了喝点水哦。
继续交流:
const res2 = await chatChain.invoke("我今天想吃方便面");
console.log(res2);
返回内容如下:
当然可以!如果你想要吃方便面,这里有一些建议:
1. **选择口味**:确保挑选你喜欢的口味,比如经典的红烧牛肉、豚骨、泡椒凤爪或者蔬菜味等。
2. **准备材料**:根据包装上的指示,准备好所需的配料,如调料包、蔬菜、鸡蛋(如果需要)等。
3. **烧水煮面**:烧一锅开水,按照方便面包装上的时间煮面,通常大约3-5分钟。
4. **添加配料**:煮好面后,把调料包、蔬菜或其他喜欢的配料加入,搅拌均匀。
5. **享受美食**:你的快速方便面就完成了,记得配一碗热水或茶,补充一下水分。
6. **健康小贴士**:虽然方便面快捷,但为了均衡营养,偶尔可以搭配一些蔬菜、水果或低脂酸奶来提升饱腹感和营养。
享用你的方便面时,别忘了适时休息,补充能量哦!
Memory
内置 Memory 的机制
BufferWindowMemory
对聊天记录加了一个滑动窗口,只会记忆 k 个对话
import { BufferWindowMemory } from "langchain/memory";
import { ConversationChain } from "langchain/chains";
import { ChatOpenAI } from "@/langchain/openai";
const model = new ChatOpenAI();
const memory = new BufferWindowMemory({ k: 1 });
const chain = new ConversationChain({ llm: model, memory: memory });
ConversationSummaryMemory
对聊天记录进行 summary,并保存在 memory 中,每次调用 chain 时,都会将 summary 作为 prompt 的一部分,即随着聊天不断生成和更新聊天记录摘要。
import { ConversationSummaryMemory } from "langchain/memory";
import { PromptTemplate } from "@langchain/core/prompts";
const memory = new ConversationSummaryMemory({
memoryKey: "summary",
llm: new ChatOpenAI({
verbose: true, // 开启 verbose 模式查看内部执行
}),
});
const model = new ChatOpenAI();
const prompt = PromptTemplate.fromTemplate(`
你是一个乐于助人的助手,尽你所能回答所有问题。
这是聊天记录的摘要:
{summary}
Human: {input}
AI:`);
const chain = new ConversationChain({ llm: model, prompt, memory, verbose: true });
const res1 = await chain.call({ input: "我是小明" });
const res2 = await chain.call({ input: "我叫什么?" });
将 BufferWindowMemory 和 ConversationSummaryMemory 结合起来,根据 token 数量,如果上下文历史过大时就切换到 summary,如果上下文比较小时就使用原始的聊天记录,也就成了 ConversationSummaryBufferMemory。
import { ChatOpenAI } from "@langchain/openai";
import { ConversationSummaryBufferMemory } from "langchain/memory";
import { ConversationChain } from "langchain/chains";
const model = new ChatOpenAI();
const memory = new ConversationSummaryBufferMemory({
llm: new ChatOpenAI(),
maxTokenLimit: 200
});
const chain = new ConversationChain({ llm: model, memory: memory, verbose: true });
原理跟前面的两个 memory 的机制类似,会计算当前完整聊天记录的 token 数,判断是否超过设置的 maxTokenLimit,如果超过则对聊天记录总结成 summary 输入进去。
ConversationSummaryBufferMemory 的思想,短聊天使用 BufferWindowMemory、长聊天就成为 ConversationSummaryMemory。
EntityMemory
EntityMemory 是一个特殊的 Memory,它将实体(实体是任何类型的对象)存储在 memory 中,并使用 LLM 生成实体的描述。EntityMemory 希望模拟的是在聊天中去生成和更新不同的实体的描述。
import { ChatOpenAI } from "@langchain/openai";
import { EntityMemory, ENTITY_MEMORY_CONVERSATION_TEMPLATE } from "langchain/memory";
import { ConversationChain } from "langchain/chains";
const model = new ChatOpenAI();
const memory = new EntityMemory({
llm: new ChatOpenAI({
verbose: true, // 开启 verbose 模式查看内部执行
}),
chatHistoryKey: "history",
entitiesKey: "entities"
});
const chain = new ConversationChain({
llm: model,
prompt: ENTITY_MEMORY_CONVERSATION_TEMPLATE,
memory: memory,
verbose: true
});
ENTITY_MEMORY_CONVERSATION_TEMPLATE 是 langchain 提供的用于 EntityMemory chat 的 默认的 prompt。也可以自定义 prompt。
进行对话:
const res1 = await chain.call({ input: "Hello, 我是小明!" });
const res2 = await chain.call({ input: "Dcat 是叮当猫,会跑会飞。" });
在 LCEL 中集成 memory
BaseMemory 是一个抽象类,不能直接使用,需要使用它的子类。所有 memory 都需要继承该接口。核心就是就是两个方法:
- loadMemoryVariables: 返回当前记忆的内容,如果有些记忆是依赖于输入的,例如 EntityMemory,就需要传入一些输入,让 memory 返回对应输入的记忆。 在 EntityMemory 的场景下,就是 memory 需要根据传入的信息提取输入中的实体,并且返回实体中相关的记忆
- saveContext: 保存当前上下文,包括输入和输出,以便下次调用。
以 BufferMemory 在 LCEL 中使用 memory。
import { BufferMemory } from "langchain/memory";
import { ChatAlibabaTongyi } from "@langchain/community/chat_models/alibaba_tongyi";
const chatModel = new ChatAlibabaTongyi({
model: "qwen-turbo", // Available models: qwen-turbo, qwen-plus, qwen-max
temperature: 1,
verbose:true
});
const memory = new BufferMemory();
const TEMPLATE = `
你是一个乐于助人的 ai 助手,尽你所能回答所有问题。
这是跟人类沟通的聊天历史:
{history}
据此回答人类的问题:
{input}
`
const prompt = ChatPromptTemplate.fromTemplate(TEMPLATE);
构建 chain:
import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";
let tempInput = ""
const chain = RunnableSequence.from([
{
input: new RunnablePassthrough(),
memoryObject: async (input) => {
const history = await memory.loadMemoryVariables({
input,
});
tempInput = input;
return history;
}
},
RunnablePassthrough.assign({
history: (input) => input.memoryObject.history
}),
prompt,
chatModel,
new StringOutputParser(),
new RunnablePassthrough({
func: async (output) => {
await memory.saveContext({
input: tempInput,
}, {
output,
})
}
}),
]);
tempInput 用于临时存储用户的 input 输入。
首先使用 new RunnablePassthrough() 对 input 进行透传,然后创建一个函数(Runnable 对象),以用户的 input 作为输入,使用 loadMemoryVariables 去加载 memory 中的数据,并将用户的输入保存到 tempInput。
加载出来的 history 对象是一个 object,其是 memory 根据用户这次 input 返回的数据,如果是 BufferMemory,则里面只有一个 history key。其他的 memory 根据不同的类型,返回的数据也是不一样的,例如 EntityMemory 就会有 history 和 entities 两个 key。
在 chain 的第二个节点,使用 RunnablePassthrough.assign() 提取出 memoryObject 中的 history 值。 assign 会在上一个节点输入的基础上,再添加新的数据。 这样后面的 prompt 就接收到了 history 和 input 两个输入值。
prompt、chatModel、new StringOutputParser() 就是基础的使用 prompt 激发 llm 输出内容,然后使用 StringOutputParser 去提取出纯文本的内容。
最后一个节点,使用 RunnablePassthrough 去执行一个函数,将用户的输入和输出使用 saveContext 存储到 memory 中。
使用下面这个技巧去打印 chain 中间传递的内容,了解数据在 chain 中是如何流动的:
[
...
prompt,
new RunnablePassthrough({
func: (input) => console.log(input)
}),
chatModel,
...
]
实现自定义 chat history
通过查看 BaseListChatMessageHistory 源码 可知真正需要实现的就是addUserMessage、addAIChatMessage、addAIMessage、clear 几个方法:
- getMessages:获取存储在 history 中所有聊天记录
- addMessage:添加单条 message
- addMessages:添加 message 数组
- clear:清空聊天记录
完整代码如下:
import { BaseListChatMessageHistory } from "@langchain/core/chat_history";
import {
BaseMessage,
StoredMessage,
mapChatMessagesToStoredMessages,
mapStoredMessagesToChatMessages,
} from "@langchain/core/messages";
import fs from "node:fs";
import path from "node:path";
export interface JSONChatHistoryInput {
sessionId: string; // sessionId 是区别于不同对话的 id,在工程中一般使用 uuid
dir: string; // dir 是存储聊天记录 json 文件的目录
}
export class JSONChatHistory extends BaseListChatMessageHistory {
/**
* BaseListChatMessageHistory 继承了 Serializable,声明 lc_namespace 是方便
* langchain 在序列化和反序列化时,找到 json 中对象对应的内置类。例如当把 message 序列化,
* 再反序列化后,打印出来依旧是对应 langchain 内部的类的实例化对象,依靠的就是这个。
*/
lc_namespace = ["langchain", "stores", "message"];
sessionId: string;
dir: string;
constructor(fields: JSONChatHistoryInput) {
super(fields);
this.sessionId = fields.sessionId;
this.dir = fields.dir;
}
/**
* 从对应的文件中读取 json 内容,然后使用 mapStoredMessagesToChatMessages 序列化成对应的 message 对象
*/
async getMessages(): Promise<BaseMessage[]> {
const filePath = path.join(this.dir, `${this.sessionId}.json`);
try {
if (!fs.existsSync(filePath)) {
this.saveMessagesToFile([]);
return [];
}
const data = fs.readFileSync(filePath, { encoding: "utf-8" });
const storedMessages = JSON.parse(data) as StoredMessage[];
return mapStoredMessagesToChatMessages(storedMessages);
} catch (error) {
console.error(`Failed to read chat history from ${filePath}`, error);
return [];
}
}
async addMessage(message: BaseMessage): Promise<void> {
const messages = await this.getMessages();
messages.push(message);
await this.saveMessagesToFile(messages);
}
async addMessages(messages: BaseMessage[]): Promise<void> {
const existingMessages = await this.getMessages();
const allMessages = existingMessages.concat(messages);
await this.saveMessagesToFile(allMessages);
}
async clear(): Promise<void> {
const filePath = path.join(this.dir, `${this.sessionId}.json`);
try {
fs.unlinkSync(filePath);
} catch (error) {
console.error(`Failed to clear chat history from ${filePath}`, error);
}
}
/**
* 使用 mapChatMessagesToStoredMessages 去对 messages 进行序列化,然后写文件到 json 文件中
* @param messages
*/
private async saveMessagesToFile(messages: BaseMessage[]): Promise<void> {
const filePath = path.join(this.dir, `${this.sessionId}.json`);
const serializedMessages = mapChatMessagesToStoredMessages(messages);
try {
fs.writeFileSync(filePath, JSON.stringify(serializedMessages, null, 2), {
encoding: "utf-8",
});
} catch (error) {
console.error(`Failed to save chat history to ${filePath}`, error);
}
}
}
测试:
import { JSONChatHistory } from "./JSONChatHistory/index.ts"
import { AIMessage, HumanMessage } from "@langchain/core/messages";
const history = new JSONChatHistory({
dir: "chat_data",
sessionId: "test"
})
await history.addMessages([
new HumanMessage("你好,我是叮当猫"),
new AIMessage("你好"),
]);
const messages = await history.getMessages();
console.log(messages);
输出内容如下:
[
HumanMessage {
lc_serializable: true,
lc_kwargs: {
content: "你好,我是叮当猫",
additional_kwargs: {},
response_metadata: {}
},
lc_namespace: [ "langchain_core", "messages" ],
content: "你好,我是叮当猫",
name: undefined,
additional_kwargs: {},
response_metadata: {}
},
AIMessage {
lc_serializable: true,
lc_kwargs: { content: "你好", additional_kwargs: {}, response_metadata: {} },
lc_namespace: [ "langchain_core", "messages" ],
content: "你好",
name: undefined,
additional_kwargs: {},
response_metadata: {}
}
]
应用在 memory 中:
import { ChatAlibabaTongyi } from "@langchain/community/chat_models/alibaba_tongyi";
import { BufferMemory } from "langchain/memory";
import { ConversationChain } from "langchain/chains";
const chatModel = new ChatAlibabaTongyi({
model: "qwen-turbo", // Available models: qwen-turbo, qwen-plus, qwen-max
temperature: 1,
});
const memory = new BufferMemory({
chatHistory: history
});
const chain = new ConversationChain({ llm: chatModel, memory: memory });
const res1 = await chain.call({ input: "我是谁?" });
console.log(res1);
输出内容如下:
{
response: "你是在说中文的“叮当猫”,这通常是中国动画《哆啦A梦》中的主角,一个来自22世纪的机器猫,以你的名字“Ding Dang”进行交流。"
}
查看 chat_data 目录下的文件,可以发现,保存了聊天记录。记录如下:
[
{
"type": "human",
"data": {
"content": "你好,我是叮当猫",
"additional_kwargs": {},
"response_metadata": {}
}
},
{
"type": "ai",
"data": {
"content": "你好",
"additional_kwargs": {},
"response_metadata": {}
}
},
{
"type": "human",
"data": {
"content": "我是谁?",
"additional_kwargs": {},
"response_metadata": {}
}
},
{
"type": "ai",
"data": {
"content": "你是在说中文的“叮当猫”,这通常是中国动画《哆啦A梦》中的主角,一个来自22世纪的机器猫,以你的名字“Ding Dang”进行交流。",
"additional_kwargs": {},
"response_metadata": {}
}
}
]
至此,通过自定义 chat history 实现把历史记录存储到本地文件和从本地文件中读取到 chat history 中,并且将自定义的 chat history 作为 memory 内置使用的 chatHistory 成功的将 memory 接入了存储中。